package Encoder;

/**
 * Title:        JpegInfo<br>
 * Description:  Given an image, sets default information about it and divides
 * it into its constituant components, downsizing those that need to be.<br>
 * @author Randr
 * 
 */

public class JpegInfo {
  /**
   * The comment.
   */
    private String comment;
  /**
   * Image height.
   */
    private int imageHeight;
  /**
   * Image width.
   */
    private int imageWidth;
  /**
   * Block width.
   */
    private int[] blockWidth;
  /**
   * Block height.
   */
    private int[] blockHeight;

// the following are set as the default
  /**
   * Precision.
   */
    public static final int precision = 8;
  /**
   * No. of components.
   */
    private final int numberOfComponents = 3;
  /**
   * Components.
   */
    private Object[] components;
  /**
   * Ids' of components.
   */
    public static final int[] compID = {1, 2, 3};
  /**
   * hsampFactor.
   */
    public static final int[] hsampFactor = {1, 1, 1};
  /**
   * vsampFactor.
   */
    public static final int[] vsampFactor = {1, 1, 1};
  /**
   * qtableNumber.
   */
    public static final int[] qtableNumber = {0, 1, 1};
  /**
   * dCtableNumber.
   */
    public static final int[] dCtableNumber = {0, 1, 1};
  /**
   * aCtableNumber.
   */
    public static final int[] aCtableNumber = {0, 1, 1};
  /**
   * lastColumnIsDummy.
   */
    private boolean[] lastColumnIsDummy = {false, false, false};
  /**
   * lastRowIsDummy.
   */
    private boolean[] lastRowIsDummy = {false, false, false};
  /**
   * ss.
   */
    private int ss;
  /**
   * se.
   */
    private int se;
  /**
   * ah.
   */
    private int ah;
  /**
   * al.
   */
    private int al;
  /**
   * compWidth.
   */
    private int[] compWidth;
  /**
   * compHeight.
   */
    private int[] compHeight;
  /**
   * maxHsampFactor.
   */
    private int maxHsampFactor;
  /**
   * maxVsampFactor.
   */
    private int maxVsampFactor;
  /**
   * imageData.
   */
    private int[] imageData;


  /**
   * Builds a new JpegInfo object.
   *
   * @param image The image.
   * @param www The width.
   * @param hhh The height.
   */
    public JpegInfo(int[] image, int www, int hhh)
    {
        this.ss = 0;
        this.se = 63;
        this.ah = 0;
        this.al = 0;
        this.imageData = image;
        this.components = new Object[numberOfComponents];
        this.compWidth = new int[numberOfComponents];
        this.compHeight = new int[numberOfComponents];
        this.blockWidth = new int[numberOfComponents];
        this.blockHeight = new int[numberOfComponents];
        this.imageWidth = www;
        this.imageHeight = hhh;
        this.comment = "JPEG Encoder";
        getYCCArray();
    }

  /**
   * Sets the comment of the Jpeg file.
   *
   * @param comment The comment of the Jpeg file.
   * @see #getComment
   */
    public void setComment(String comment) {
        comment.concat(comment);
    }

  /**
   * Gets the comment of the Jpeg file.
   *
   * @return comment The comment of the Jpeg file.
   * @see #setComment
   */
    public String getComment() {
        return comment;
    }

    /**
     * Creates and fills three arrays, yY, Cb, and Cr using the input image.
     */

    private void getYCCArray()
    {
      int[] values = new int[imageData.length];
      int r = 0;
      int g = 0;
      int b = 0;
      int y = 0;
      int x = 0;
      // In order to minimize the chance that grabPixels will throw an exception
      // it may be necessary to grab some pixels every few scanlines and process
      // those before going for more.  The time expense may be prohibitive.
      // However, for a situation where memory overhead is a concern, this may be
      // the only choice.
      System.arraycopy(this.imageData,0,values,0,this.imageData.length);
      maxHsampFactor = 1;
      maxVsampFactor = 1;
      for (y = 0; y < numberOfComponents; y++)
      {
          maxHsampFactor = Math.max(maxHsampFactor, hsampFactor[y]);
          maxVsampFactor = Math.max(maxVsampFactor, vsampFactor[y]);
      }
      for (y = 0; y < numberOfComponents; y++)
      {
          if(imageWidth%8 != 0)
          {
              compWidth[y] = (( ((int) Math.ceil(imageWidth/8.0))*8 )/maxHsampFactor)*hsampFactor[y];
          }
          else
          {
              compWidth[y] = ((imageWidth)/maxHsampFactor)*hsampFactor[y];
          }
          if (compWidth[y] != ((imageWidth/maxHsampFactor)*hsampFactor[y]))
          {
              lastColumnIsDummy[y] = true;
          }
          // results in a multiple of 8 for compWidth
          // this will make the rest of the program fail for the unlikely
          // event that someone tries to compress an 16 x 16 pixel image
          // which would of course be worse than pointless
          blockWidth[y] = (int) Math.ceil((double) compWidth[y]/8.0);
          if(imageHeight%8 != 0)
          {
              compHeight[y] = (( ((int) Math.ceil(imageHeight/8.0))*8 )/maxVsampFactor)*vsampFactor[y];
          }
          else
          {
              compHeight[y] = ((imageHeight)/maxVsampFactor)*vsampFactor[y];
          }
          if (compHeight[y] != ((imageHeight/maxVsampFactor)*vsampFactor[y]))
          {
              lastRowIsDummy[y] = true;
          }
          blockHeight[y] = (int) Math.ceil((double) compHeight[y]/8.0);
      }

      float[][] yY = new float[compHeight[0]][compWidth[0]];
      float[][] cr1 = new float[compHeight[0]][compWidth[0]];
      float[][] cb1 = new float[compHeight[0]][compWidth[0]];
      int index = 0;
      for (y = 0; y < imageHeight; ++y)
      {
          for (x = 0; x < imageWidth; ++x)
          {
              r = ((values[index] >> 16) & 0xff);
              g = ((values[index] >> 8) & 0xff);
              b = (values[index] & 0xff);

              yY[y][x] = (float)((0.299 * r + 0.587 * g + 0.114 * b));
              cb1[y][x] = 128 + (float)((-0.16874 * r - 0.33126 * g + 0.5 * b));
              cr1[y][x] = 128 + (float)((0.5 * r - 0.41869 * g - 0.08131 * b));
              index++;
          }
      }

      components[0] = yY;
      components[1] = cb1;
      components[2] = cr1;
    }
    /**
     * Gets the {@link #numberOfComponents} value.
     *
     * @return the {@link #numberOfComponents} value
     */
    public int getNumberOfComponents() {
      return this.numberOfComponents;
    }
    /**
     * Gets the value from the {@link #blockWidth} array
     * from the specified index.
     *
     * @param index the index from the {@link #blockWidth} array
     * @return the value from the {@link #blockWidth} array
     * from the specified index.
     */
    public int getBlockWidth(int index) {
      return this.blockWidth[index];
    }
    /**
     * Gets the value from the {@link #blockWidth} array
     * from the specified index.
     *
     * @param index the index from the {@link #blockWidth} array
     * @return the value from the {@link #blockWidth} array
     * from the specified index.
     */
    public int getBlockHeight(int index) {
      return this.blockHeight[index];
    }
    /**
     * Gets the value from the {@link #blockWidth} array
     * from the specified index.
     *
     * @param index the index from the {@link #blockWidth} array
     * @return the value from the {@link #blockWidth} array
     * from the specified index.
     */
    public Object getComponent(int index) {
      return this.components[index];
    }
    /**
     * Gets the {@link #numberOfComponents} value.
     *
     * @return the {@link #numberOfComponents} value
     */
    public int getImageHeight() {
      return this.imageHeight;
    }
    /**
     * Gets the {@link #numberOfComponents} value.
     *
     * @return the {@link #numberOfComponents} value
     */
    public int getImageWidth() {
      return this.imageWidth;
    }
    /**
     * Gets the value from the {@link #blockWidth} array
     * from the specified index.
     *
     * @param index the index from the {@link #blockWidth} array
     * @return the value from the {@link #blockWidth} array
     * from the specified index.
     */
    public boolean getLastColumnIsDummy(int index){
      return this.lastColumnIsDummy[index];
    }
    /**
     * Gets the value from the {@link #blockWidth} array
     * from the specified index.
     *
     * @param index the index from the {@link #blockWidth} array
     * @return the value from the {@link #blockWidth} array
     * from the specified index.
     */
    public boolean lastRowIsDummy(int index){
      return this.lastRowIsDummy[index];
    }
    /**
     * Gets the {@link #numberOfComponents} value.
     *
     * @return the {@link #numberOfComponents} value
     */
    public int getSs() {
      return this.ss;
    }
    /**
     * Gets the {@link #numberOfComponents} value.
     *
     * @return the {@link #numberOfComponents} value
     */
    public int getSe() {
      return this.se;
    }
    /**
     * Gets the {@link #numberOfComponents} value.
     *
     * @return the {@link #numberOfComponents} value
     */
    public int getAh(){
      return this.ah;
    }
    /**
     * Gets the {@link #numberOfComponents} value.
     *
     * @return the {@link #numberOfComponents} value
     */
    public int getAl(){
      return this.al;
    }
    /**
     * Gets the value from the {@link #blockWidth} array
     * from the specified index.
     *
     * @param index the index from the {@link #blockWidth} array
     * @return the value from the {@link #blockWidth} array
     * from the specified index.
     */
    public int getCompWidth(int index){
      return this.compWidth[index];
    }
    /**
     * Gets the value from the {@link #blockWidth} array
     * from the specified index.
     *
     * @param index the index from the {@link #blockWidth} array
     * @return the value from the {@link #blockWidth} array
     * from the specified index.
     */
    public int getCompHeight(int index){
      return this.compHeight[index];
    }
}
